#pragma once
//Some helper functions
#include <iostream>
#include <cblas.h>
#include <cmath>
#include <cstdint> // For uint8_t
#include <type_traits>
#include <fstream>

#include "json/json.hpp"
using json = nlohmann::json;
using namespace std;

void printJson(const json& j, const std::string& prefix = "|-") {
    if (j.is_object()) {
        for (auto it = j.begin(); it!= j.end(); ++it) {
            std::string new_prefix = prefix.empty()? it.key() : prefix + it.key();
            printJson(it.value(), new_prefix);
        }
    } else if (j.is_array()) {
        for (size_t i = 0; i < j.size(); ++i) {
            std::string new_prefix = prefix.empty()? "[" + std::to_string(i) + "]" : prefix + "[" + std::to_string(i) + "]";
            printJson(j[i], new_prefix);
        }
    } else {
        std::cout << prefix << ": " << j << std::endl;
    }
}

int load_config(string path, json& config){
    //加载数据集配置
    cout << "Load configuration in: " << path << endl;
    std::ifstream config_fd(path);
    if (!config_fd.is_open()) {
        std::cerr << "无法打开配置文件" << path << std::endl;
        //退出
        return -1;
    }
    config_fd >> config;
    return 0;
}

template <typename T>
float l2_distance(T* A, T* B, int dim){
    float result = 0;
	T temp = 0;
	for(int i=0; i<dim; i++){
		temp = A[i]-B[i];
		temp = temp*temp;
		result += temp;
	}
	result = sqrt(result);
	return result;
}

float l2_distance_openblas(float* A, float* B, int dim) {
    // 计算差值向量
    float* diff = new float[dim];
    for (int i = 0; i < dim; ++i) {
        diff[i] = A[i] - B[i];
    }

    // 使用 OpenBLAS 计算差值向量的点积（平方和）
    float dot_product = cblas_sdot(dim, diff, 1, diff, 1);

    // 释放差值向量的内存
    delete[] diff;

    // 计算平方根得到 L2 距离
    return std::sqrt(dot_product);
}

float l2_distance_general(float* A, float* B, int dim){
    // cout << "l2_distance_general: float and float" << endl;
    // 计算差值向量
    float* diff = new float[dim];
    for (int i = 0; i < dim; ++i) {
        diff[i] = A[i] - B[i];
    }

    // 使用 OpenBLAS 计算差值向量的点积（平方和）
    float dot_product = cblas_sdot(dim, diff, 1, diff, 1);

    // 释放差值向量的内存
    delete[] diff;

    // 计算平方根得到 L2 距离
    // return std::sqrt(dot_product);
    return dot_product;
}

float l2_distance_general(float* A, uint8_t* B, int dim) {
    float* diff = new float[dim];
    
    // 计算差值向量
    for (int i = 0; i < dim; ++i) {
        diff[i] = A[i] - static_cast<float>(B[i]);
    }
    
    // 调用OpenBLAS计算点积
    float dot_product = cblas_sdot(dim, diff, 1, diff, 1);
    
    delete[] diff;
    return dot_product;
}

float l2_distance_general(uint8_t* A, float* B, int dim){
    return l2_distance_general(B, A, dim);
}

float l2_distance_general(uint8_t* A, uint8_t* B, int dim) {
    // 计算差值向量 C = A - B
    float* C = new float[dim];
    for (int i = 0; i < dim; ++i) {
        C[i] = static_cast<float>(A[i]) - static_cast<float>(B[i]);
    }
    
    // 计算L2范数的平方：dot(C, C)
    float dot_product = cblas_sdot(dim, C, 1, C, 1);
    
    delete[] C;
    return dot_product;
}

//万能打印数组的函数
template <class T>
void print_array(const T* arr, int size) {
    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}

template <class T>
void print_array(std::vector<T>& arr, int size) {
    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}

template <class T>
T ann_min(T a, T b){
    return a<b?a:b;
}